#include <math.h>
#include <time.h>
#include <windows.h>
#include "pThreads/semaphore.h"
#include <vector>
#include "CSemaphore.h"
#include <algorithm>
#include "MySQL/mysql.h"

#include "CNpc.h"
#include "CUtil.h"
#include "CConsole.h"
#include "CGlobal.h"
#include "CDb.h"
#include "CSpell.h"
#include "CActionPacket.h"
#include "CMessagePacket.h"

// loads a spell from the db
CSpell::CSpell(unsigned short spellID) {
	char query[8192];
	unsigned long count;
	MYSQL_RES * res;
	MYSQL_ROW row;

	sprintf(query,"SELECT sl.spellID, sl.spellName, sl.mpCost, sl.magicElement, sl.castTime, sl.defaultMsgType, sl.magicGroup, sl.allowedTargets, sl.magicType, sl.animationID, st.animationID, sl.recastTime, sl.isAOE FROM spell_list AS sl LEFT JOIN spell_type AS st ON (sl.magicGroup = st.magicGroup) WHERE sl.spellID = %i ",spellID);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn
	if(!res)
	{
		CConsole::outErr("Error looking up spell");
	} else if(!mysql_num_rows(res)){
		mysql_free_result(res);
		CConsole::outErr("Error looking up spell");	
	} else {
		row = mysql_fetch_row(res);
		this->m_spellID = atoi(row[0]);
		memcpy(&(this->m_spellName), row[1], 50);
		this->m_mpCost = atoi(row[2]);
		this->m_element = atoi(row[3]);
		this->m_castTime = atof(row[4]);
		this->m_defaultMsg = atoi(row[5]);
		this->m_magicGroup = atoi(row[6]);
		this->m_allowedTargets = atoi(row[7]);
		this->m_spellSkill = atoi(row[8]);
		this->m_animID = atoi(row[9]);
		this->m_spellType = atoi(row[10]);
		this->m_recastTime = atof(row[11]);
		this->m_range = atoi(row[12]);
		
		mysql_free_result(res);
	}

	
}

CSpell::~CSpell(void)
{
}


void CSpell::setAnimation(unsigned short animation) {
	this->m_animID = animation;
}

// returns the cast time of the spell
double CSpell::getCastTime() {
	return this->m_castTime;
}

// returns the mp cost of the spell
unsigned short CSpell::getMPCost() {
	return this->m_mpCost;
}

// returns the element of the spell, including physical blue magic
unsigned short CSpell::getElement() {
	return this->m_element;
}

// returns the name of the spell
char * CSpell::getSpellName() {
	return this->m_spellName;
}

// returns the id of the spell 
unsigned short CSpell::getSpellID() {
	return this->m_spellID;
}

// returns the animation ID of the spell
unsigned short CSpell::getSpellAnimID() {
	return this->m_animID;
}

// returns the casting-animation ID of the spell
unsigned short CSpell::getSpellType() {
	return this->m_spellType;
}

// set the spell caster
void CSpell::setCaster(CBaseEntity * pCaster) {
	this->m_pCaster = pCaster;
}

// set the spell target ID
void CSpell::setSpellTargetID(unsigned int targetID) {
	this->m_targetID = targetID;
}

// return the ID of the maintarget
unsigned int CSpell::getSpellTargetID() {
	return this->m_targetID;
}

// returns the default message of the spell
unsigned int CSpell::getSpellDefaultMsgType() {
	return this->m_defaultMsg;
}

// sets the main target of the spell
void CSpell::setMainTarget(CBaseEntity * pTarget) {
	this->m_pMainTarget = pTarget;
}

// returns a pointer to the main target of the spell
CBaseEntity * CSpell::getMainTarget() {
	return this->m_pMainTarget;
}

// returns a pointer to the caster
CBaseEntity * CSpell::getCaster() {
	return this->m_pCaster;
}

// returns the recast time of a spell
double CSpell::getRecastTime() {
	return this->m_recastTime;
}

// returns the position from which the cast has started
struct position CSpell::getCastOrigin() {
	return this->m_castOrigin;
}

// returns the skill type of a spell
unsigned char CSpell::getSpellSkill() {
	return this->m_spellSkill;
}

// sets the position from which the cast has started
void CSpell::setCastOrigin(struct position p) {
	this->m_castOrigin = p;
}

// returns true if the spell has an AOE effect
bool CSpell::isAOESpell() {
	return (this->m_range == 1);
}

// show the casting animation
void CSpell::startCast() {
	
	this->m_pCaster->m_isCasting = true;
	CActionPacket* attackPacket=new CActionPacket();
	
	attackPacket->setActorId(this->m_pCaster->getId());
	attackPacket->setCategorie(8);
	attackPacket->setStartAnimationCategorie(0x18);
	attackPacket->setStartAnimation(this->getSpellType());
	attackPacket->setParameter1(0x0163);
	attackPacket->setUnknown(0x00000004);
	
	apActionEntry newEntry;
	memset(&newEntry,0,sizeof(newEntry));

	newEntry.unknownValue = 0x00;
	newEntry.targetAnimationID = 0x01;
	newEntry.unknownValue2 = 0x000;

	newEntry.messageType = 0x0147;
	
	newEntry.additionalMessages = 0x00000000;
	newEntry.dynamisTimerFlag = 0x0;
	newEntry.parameter2 = this->getSpellID();

	attackPacket->setTargetID(1,this->m_targetID);
	attackPacket->setActionEntry(1,1,&newEntry);


	attackPacket->finalizePacket();

	((CChar*)this->m_pCaster)->insertPacket(CHAR_INRANGE_SELF,*attackPacket);
}

// interrupt a spell
void CSpell::interrupt() {
		
	CChar * schar = (CChar*)this->getCaster();

	this->m_pCaster->m_isCasting = false;
	CActionPacket* actionPacket=new CActionPacket();
	actionPacket->setActorId(schar->getId());
	actionPacket->setCategorie(8);//Magic - End
	actionPacket->setStartAnimationCategorie(0x1c);//Magic
	actionPacket->setStartAnimation(this->getSpellType());
		
	actionPacket->setParameter1(0x73);
	actionPacket->setUnknown(0x00000000);
	apActionEntry newEntry;
	memset(&newEntry,0,sizeof(newEntry));
	newEntry.unknownValue=0x09;
	newEntry.targetAnimationID = 0;
	newEntry.unknownValue2=0x000;
	newEntry.parameter2=3;
	newEntry.messageType=0;
	newEntry.additionalMessages=0x00000000;
	newEntry.dynamisTimerFlag=0x0;
	actionPacket->setTargetID(1,this->getSpellTargetID());
	actionPacket->setActionEntry(1,1,&newEntry);

	actionPacket->finalizePacket();
	schar->insertPacket(CHAR_INRANGE_SELF, *actionPacket);

}


// return true if the spell is a teleport spell
bool CSpell::isTeleportSpell() {
	
	if( this->getSpellID() == 81 ||
		this->getSpellID() == 82 ||
		this->getSpellID() == 83 ||
		this->getSpellID() == 120 ||
		this->getSpellID() == 121 ||
		this->getSpellID() == 122 ||
		this->getSpellID() == 123 ||
		this->getSpellID() == 124 ||
		this->getSpellID() == 139 ||
		this->getSpellID() == 241 ||
		this->getSpellID() == 261 ||
		this->getSpellID() == 262 ||
		this->getSpellID() == 263 ) {
	            
			return true;

	}

	return false;

}

// Return a bitmask with the allowed target-types
unsigned short CSpell::getAllowedTargets() {
	return this->m_allowedTargets;
}

// pushes a target pointer onto the target-list
void CSpell::addTarget(CBaseEntity * pTarget) {
	this->m_targetList.push_back(pTarget);
}

// returns true if the target is valid to the spell
bool CSpell::isValidTarget(CBaseEntity * pTarget) {

	CChar * schar = (CChar*)this->getCaster();

	if(pTarget->getObjectType() == OBJ_NPC) {
		return false;
	}

	if(!(this->getAllowedTargets() & TARGET_SELF)) {
		if(this->getCaster()->getId() == this->getMainTarget()->getId()) {
			return false;
		}
	}

	if(!(this->getAllowedTargets() & TARGET_MOB)) {
		if((this->getMainTarget()->isMob())) {
			return false;
		}
	}
			
	if(!(this->getAllowedTargets() & TARGET_PLAYER)) {

		if(this->getMainTarget()->isPlayer()) {

			if((this->getAllowedTargets() & TARGET_PLAYER_DEAD) && this->getMainTarget()->isDead()) {
				return true;
			} else if((this->getAllowedTargets() & TARGET_PLAYER_PARTY)) {
				if( ((CChar*)this->getCaster())->isPartyMember(((CChar*)this->getMainTarget()))) {
					return true;
				}
			} else if(this->getCaster()->getId() == this->getMainTarget()->getId()) {
				if(!(this->getAllowedTargets() & TARGET_SELF)) {
					return false;
				}
			} else {
				return false;
			}
		}
	}

	if((this->getAllowedTargets() & TARGET_PLAYER)) {
		if(this->getMainTarget()->isDead() && !(this->getAllowedTargets() & TARGET_PLAYER_DEAD)) {
			return false;	
		}
	}

	return true;
}


void CSpell::setSpellDefaultMsgType(unsigned int msgID) {
	this->m_defaultMsg = msgID;
}

void CSpell::setCurrentTargetNum(unsigned short ctn) {
	this->m_currentTargetNum = ctn;
}

unsigned short CSpell::getCurrentTargetNum() {
	return this->m_currentTargetNum;
}